package com.cskaoyan.jdbc;

import com.cskaoyan.jdbc.util.JdbcUtil;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @ClassName Batch_8
 * @Description: TODO
 * @Author 远志 zhangsong@cskaoyan.onaliyun.com
 * @Date 2023/2/7 10:10
 * @Version V1.0
 **/
public class Batch_8 {

    //定义了一个常量，后续不管是何种处理方式，全部执行相同次数的操作
    private static final int COUNT_TIMES = 3000;

    public static void main(String[] args) {
        Connection connection = null;
        try {
            //作用是获取connection对象
            connection = JdbcUtil.getConnection();
            //常规的利用statement来进行批量操作
            long t1 = System.currentTimeMillis();
            ordinaryStatement(connection);
            long t2 = System.currentTimeMillis();
            //常规的利用prepareStatement来进行批量操作
            ordinaryPrepareStatement(connection);
            long t3 = System.currentTimeMillis();
            //利用statement的批处理来进行批量操作
            batchStatament(connection);
            long t4 = System.currentTimeMillis();
            //利用preparestatement的批处理来进行批量操作
            batchPrepareStatement(connection);
            long t5 = System.currentTimeMillis();
            System.out.println("常规statement花费的时间" + (t2 - t1));
            System.out.println("常规preparedStatement花费的时间" + (t3 - t2));
            System.out.println("statement批处理花费的时间" + (t4 - t3));
            System.out.println("prepareStatement批处理花费的时间" + (t5 - t4));

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    private static void batchPrepareStatement(Connection connection) throws SQLException {
        PreparedStatement preparedStatement = connection.prepareStatement("insert into user values (null, ?, ?)");
        for (int i = 0; i < COUNT_TIMES; i++) {
            preparedStatement.setString(1, i + "");
            preparedStatement.setString(2, i + "");
            //为何preparedStatement相较于statement的批处理行为有较大区别？原因主要在于preparedstatement在创建时已经确定好了sql语句的类型，所以不需要像statement一样，再次填充sql语句。只需要填充参数即可
            //之前常规的方式是每填充一组参数，那么便发送到数据库进行执行；那么现在是填充多组之后，一并发送到数据库执行
            preparedStatement.addBatch();
            if(i % 1000 == 0 && i != 0){
                preparedStatement.executeBatch();
                preparedStatement.clearBatch();
            }
        }
        preparedStatement.executeBatch();
        preparedStatement.close();
    }

    /**
     * 无论何种方式的批处理：整体的思路流程都是一致的。那就是之前我每次搬一块砖；现在我每次搬运几百块砖
     * 那么便可以节省很多的时间
     * @param connection
     */
    private static void batchStatament(Connection connection) throws SQLException {
        Statement statement = connection.createStatement();
        for (int i = 0; i < COUNT_TIMES; i++) {
            //将sql语句装入搬运车中，然后一次性传输到数据库
            statement.addBatch("insert into user values (null ,'" + i + "','" + i + "')");
            //但是，如果数据量很大的情况下，那么也有可能会出现车子容量不足的情况，所以，可以先转运一部分
            if(i % 1000 == 0 && i != 0){
                //每1000次转运一次
                //将满足条件的sql语句发送到数据库，执行一次
                statement.executeBatch();
                //把这次已经执行完毕的缓存的sql清空
                statement.clearBatch();
            }
        }
        //因为最后一次整除过后剩余的部分，没法在if条件中进行执行，所以最终，我们加上这条sql语句
        statement.executeBatch();
        statement.close();
    }

    private static void ordinaryPrepareStatement(Connection connection) throws SQLException {
        //注意和statement的区别；prepareStatement在创建时必须指定sql语句，这是和statement的不同
        //PreparedStatement继承自statement对象，所以也是和数据库进行交互传递sql语句的
        PreparedStatement preparedStatement = connection.prepareStatement("insert into user values (null, ?, ?)");
        for (int i = 0; i < COUNT_TIMES; i++) {
            //设置参数i + "" 为什么加空字符串？ 因为我们这个方法是填充string，但是i是整数，需要将i转换成字符串
            preparedStatement.setString(1, i + "");
            preparedStatement.setString(2, i + "");
            preparedStatement.executeUpdate();
        }
        preparedStatement.close();
    }

    private static void ordinaryStatement(Connection connection) throws SQLException {
        //利用connection对象进一步创建statement对象，用来去发送sql语句
        Statement statement = connection.createStatement();
        for (int i = 0; i < COUNT_TIMES; i++) {
            //双引号是java语言用来截断不同部分的字符串的；单引号主要是给数据库用的；因为插入到数据库中的字符串需要用引号括起来
            statement.executeUpdate("insert into user values (null ,'" + i + "','" + i + "')");
        }
        statement.close();
    }
}
